import java.util.Calendar;
import java.util.Timer;

import javax.xml.bind.JAXBException;

public class Improvise {
	public int count=0;
	public String name=null; // temporary
	public String[] styles = new String[20]; // TODO: store all loaded styles,strength 
	public int[] styleStrengths = new int[20];
	public int stylePointer=0;
	public int scale=0;
	public boolean major=true;
	public int[] pitchProbArray = new int[1];
	public int[] notesInput = new int[1];
	public int[] notesOutput = new int[1];
	public int[] volumeOutput = new int[1];
	public int[] durationOutput = new int[1];
	public int pitchChoice;
	public int pitch;
	public int lastPitch=0;
	public int lastPitchTr=0;
	public Timer timer;
	public TimerNewNote timerNewNote;
	public TimerNewNotePlayback timerNewNotePlayback;
	public int waitTime = 100;
	public boolean stop = true;
	public boolean listening = false;
	public long lastInputOnset=0;
	public boolean playing=false;
	public boolean soloing=false;// human player is soloing?
	public boolean learnSoloing=false;
	public int playbackPointer=0;
	public int[] transposition = {-7,-5,0,5,7};
	public int transpositionIndex=0;

	public int playbackCounter;
	public int improviseCounter;
	
	public int variability=35;
	
	public boolean revPitch = false;
	public boolean revDuration = false;
	
	public int pointer = 3;
	
	int[][] learnArray = new int[0][3];
	
	int revPointer=0;

	
	public void start() throws JAXBException {
		
		if (Main.learnArrayPointer>2) {
			
			if (Math.random()<.5) {
				System.out.println("REVERSE PITCH");
				revPitch = true;
			}
			else
			{
				revPitch = false;
			}
			if (Math.random()<.5) {
				revDuration = true;
			}
			else {
				revDuration = false;
			}

			revPointer = 0;
			
			playbackPointer=Main.learnArrayPointer-1;
			float targetTime = Main.improvTime;
			System.out.println("Main.improvTime="+Main.improvTime);
			float targetTimePointer=0;
			while (targetTimePointer<targetTime&&playbackPointer>0)
			{
				targetTimePointer=targetTimePointer+Main.learnArray[playbackPointer][2];
//				System.out.println("Main.learnArray[playbackPointer][2]="+Main.learnArray[playbackPointer][2]);
				playbackPointer=playbackPointer-1;
			}
			
//			playbackPointer=0;
			transpositionIndex=(int) (Math.random()*5);
			startPlayback();
		}
		else 
		{
			pitch = PitchTranslation.translate(Main.learnArray[playbackPointer][0]+transposition[transpositionIndex],lastPitch,lastPitchTr);					
			Object[] noteArray = {pitch,Main.velocity};
			OSC.main(noteArray,"/mididata"); // *output the note!
			
			System.out.println("learnarraypointer="+Main.learnArrayPointer);
		}
	}
	public void startPlayback()
	{
		
		double calc = variability*Math.random();
		boolean toImprovise = calc<playbackCounter;
		toImprovise = false;
//		System.out.println("**************Variability="+calc+" Playback counter="+playbackCounter);
		if(toImprovise)
		{
			playbackCounter=0;
			try {
				startImprovise();
			} catch (JAXBException e) {
				e.printStackTrace();
			}
		}
		else
		{
			playbackCounter=playbackCounter+1;
			if (playbackPointer<Main.learnArrayPointer&&playbackPointer>=0&&stop==false&&soloing==false)
			{
				if (Main.learnArray[playbackPointer+1][2]<2000)
				{
					waitTime=Main.learnArray[playbackPointer+1][2];
				}
				else
				{
					waitTime=500;
				}
				if (revPitch) {
					pitch = PitchTranslation.translate(Main.learnArray[Main.learnArrayPointer-1-revPointer][0]+transposition[transpositionIndex],lastPitch,lastPitchTr);
				}
				else {
					pitch = PitchTranslation.translate(Main.learnArray[playbackPointer][0]+transposition[transpositionIndex],lastPitch,lastPitchTr);					
				}
				
				Object[] noteArray = {pitch,Main.velocity};

				if (pitch>90) {
					pitch=72+pitch%12;
				}
				System.out.println("note out: "+pitch);
				OSC.main(noteArray,"/mididata"); // *output the note!
				
				int[] temp = notesOutput;
				notesOutput = new int[notesOutput.length+1];
				System.arraycopy(temp, 0, notesOutput, 0, temp.length);
				notesOutput[notesOutput.length-1]=pitchChoice;
				
				temp = volumeOutput;
				volumeOutput = new int[volumeOutput.length+1];
				System.arraycopy(temp, 0, volumeOutput, 0, temp.length);
				volumeOutput[volumeOutput.length-1]=Main.learnArray[playbackPointer][1];
				
				temp = durationOutput;
				durationOutput = new int[durationOutput.length+1];
				System.arraycopy(temp, 0, durationOutput, 0, temp.length);
				if (revDuration) {
					durationOutput[durationOutput.length-1]=Main.learnArray[Main.learnArrayPointer-1-revPointer][2];					
				}
				else {
					durationOutput[durationOutput.length-1]=Main.learnArray[playbackPointer][2];	
				}
				
				playbackPointer++;
				revPointer++;
				
				timer = new Timer();
				timerNewNotePlayback = new TimerNewNotePlayback();
				timer.schedule(timerNewNotePlayback, (int) (waitTime));
			}
			else
			{			
				try {
					startImprovise();
				} catch (JAXBException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	public void startImprovise() throws JAXBException {
		boolean toPlayback = variability*Math.random()<improviseCounter;
		if(toPlayback)
		{
			improviseCounter=0;
			startPlayback();
		}

		else
		{
			improviseCounter=improviseCounter+1;
			if (stop==false&&soloing==false){
				decideStyle();
				int[] notes = null;
				int[] volume = null;
				int[] duration = null;
				if (name.equalsIgnoreCase("me")){
					notes = Main.learnArray[0];
					volume = Main.learnArray[1];
					duration = Main.learnArray[2];
					
				}
				else {
					notes = XML.getNotes(name);
					volume = XML.getVolume(name);
					duration = XML.getDuration(name);
				}
				if (count==0&&notes.length>6){
					int choice = (int) (Math.random()*(notes.length-4))+1;
					
					Object[] noteArray = {notes[choice]+scale,volume[choice]};
					int[] noteArrayInt = {notes[choice]+scale,volume[choice]};
					
					lastPitch=notes[choice]+scale;
					lastPitchTr=notes[choice]+scale;
					
					notesOutput[0]=notes[choice]+scale+transposition[transpositionIndex];
					volumeOutput[0]=volume[choice];
					
					OSC.main(noteArray,"/mididata");
//					Main.armHandling.receiveNote(noteArrayInt);
					
					int durationChoice = duration[choice];
					durationOutput[0]=duration[choice];	
		
				}
				else if (notes.length>6){
					pitchProbArray = new int[notes.length-4];
					for (int i=3;i<notes.length-4;i++){
						if (notes[i-1]==notesOutput[notesOutput.length-1]){
							pitchProbArray[i]=pitchProbArray[i]+1;
						}
						if (notesOutput.length>1){
							if (notes[i-1]==notesOutput[notesOutput.length-1]&&notes[i-2]==notesOutput[notesOutput.length-2]){
								pitchProbArray[i]=pitchProbArray[i]+100;
							}
						}
						if (notesOutput.length>2){
							if (notes[i-1]==notesOutput[notesOutput.length-1]&&notes[i-2]==notesOutput[notesOutput.length-2]&&notes[i-3]==notesOutput[notesOutput.length-3]){
							pitchProbArray[i]=pitchProbArray[i]+10000;
		//					System.out.println("*****************");
							}
						}
					}
					
					int choice=decision(pitchProbArray);
	//				pointer = pointer + 1;
	//				int choice = pointer;
					if(notes[choice]<30){
						System.out.println("ERROR on NOTEOUT: "+notes[choice]);
						choice =(int) (Math.random()*(notes.length-2)+1);
						System.out.println("new note: "+notes[choice]);
					}
					
					int pitchChoice = notes[choice]+scale+transposition[transpositionIndex];
					pitch = PitchTranslation.translate(pitchChoice,lastPitch,lastPitchTr);
					lastPitch=pitch;
					lastPitchTr=pitchChoice;
	//				System.out.println("pitchOut="+pitch);
		//			System.out.println("count="+count);
					int volumeChoice = volume[choice];
					int durationChoice = duration[choice-1];
					
					while(pitch<48){
						pitch=pitch+12;
					}
					while(pitch>94){
						pitch=pitch-12;
					}
					
					Object[] noteArray = {pitch,volumeChoice};
	//				int[] noteArrayInt = {notes[choice],volume[choice]};
					OSC.main(noteArray,"/mididata");
	//				Main.armHandling.receiveNote(noteArrayInt);
					
					TimeTranslation timeTranslation=new TimeTranslation();
					timeTranslation.playbackDiff=durationChoice;
					timeTranslation.run();
					
	
								
					int[] temp = notesOutput;
					notesOutput = new int[notesOutput.length+1];
					System.arraycopy(temp, 0, notesOutput, 0, temp.length);
					notesOutput[notesOutput.length-1]=pitchChoice;
					
					temp = volumeOutput;
					volumeOutput = new int[volumeOutput.length+1];
					System.arraycopy(temp, 0, volumeOutput, 0, temp.length);
					volumeOutput[volumeOutput.length-1]=volume[choice];
					
					temp = durationOutput;
					durationOutput = new int[durationOutput.length+1];
					System.arraycopy(temp, 0, durationOutput, 0, temp.length);
					durationOutput[durationOutput.length-1]=0;
					System.out.println("?");
					
					
				}
				count=count+1; // update count
			}

			if (!stop)
			{
				int wait = 200;
				if (Main.learnArrayPointer>0){
					wait = Main.learnArray[improviseCounter%Main.learnArrayPointer][2];
				}
				System.out.println("setting timer again wait for: "+(int) (waitTime));
				timer = new Timer();
				timerNewNote = new TimerNewNote();
				timer.schedule(timerNewNote, (int)(waitTime*1.5));
			}
		}
	}
	public int decision(int[] valuesArray){
		int sum = sumValues(valuesArray);
		int countTotal = (int) (Math.random()*sum)+1;
		int index=0;
		int counter=0;
		while (counter<countTotal&&index<valuesArray.length-1){
			counter=counter+valuesArray[index];
			if (counter<countTotal){
				index=index+1;
			}
		}
		return index;
	}
	public int sumValues(int[] valuesArray){
		int sum=0;
		for (int i=0; i<valuesArray.length; i++){
			sum = sum+valuesArray[i];
		}	
		return sum;
	}
	
	public void incomingNote(int note){
		int[] temp = notesInput;
		notesInput = new int[notesInput.length+1];
		System.arraycopy(temp, 0, notesInput, 0, temp.length);
		notesInput[notesInput.length-1]=note;
	}
	public void decideStyle(){
		name = styles[decision(styleStrengths)];
	}
}
